出处:掘金

原作者:ErpanOmer


干前端的,天天都在跟 CSS 打交道。以前我们写 CSS,要是遇到复杂点的场景,比如“当一个卡片里有图片时,给卡片加个边框”,或者要给一长串不同的标题应用同一个样式,那代码写起来,说实话,挺啰嗦的

但时代变了。CSS 自己也越来越“聪明”

今天想聊的这三个选择器 —— :has() , :is() , :where(),可能有些同学还不太熟,但我敢说,一旦你开始用它们,绝对会有一种“卧槽,原来 CSS 可以这么写?”的舒爽感。它们不是什么花里胡哨的技能,而是能实实在在提升我们日常编码效率和代码可读性的“神器”

:has() - “父选择器”

:has() 绝对是这三个里面最重要的选择器。我们一直想要一个能根据子元素来选择父元素的方法,现在,它来了

简单说,:has(selector) 的意思是:选择包含 selector 所匹配元素的那个元素

听起来有点绕,直接看例子

场景

当卡片内有图片时,改变卡片样式

以前我们怎么写?

我们没法用 CSS 直接判断,通常得用 JS 去遍历 DOM,给有图片的卡片加个 class

// JS to add a class
document.querySelectorAll('.card').forEach(card => {
  if (card.querySelector('img')) {
    card.classList.add('card-with-image');
  }
});
/* Then style the class */
.card-with-image {
  border: 2px solid #61dafb;
}

这太麻烦了,样式的问题还得 JS 来帮忙

现在用 :has() 怎么写?(一行 CSS 搞定)

/* Select any .card that has an <img> inside it */
.card:has(img) {
  border: 2px solid #61dafb;
  padding: 10px;
}

看到了吗?就这么简单。 当 .card 元素“拥有”一个 img 子孙时,这个样式就生效了

更多玩法

表单校验高亮:当输入框后面紧跟着一个 .error 提示时,让输入框变红

/* Select an input that has an adjacent sibling .error */
input:has(+ .error-message) {
  border-color: red;
}

布局调整:当一个容器里有超过 5 个子项时,改变布局

/* Select a .grid-container that has at least 6 children */
.grid-container:has(div:nth-child(6)) {
  grid-template-columns: repeat(3, 1fr); /* 切换到三列布局 */
}

:has() 的出现,让 CSS 的逻辑表达能力提升了一个维度,很多以前必须 JS 出马的场景,现在 CSS 自己就能搞定

:is():where() - 别再写一长串重复的选择器了

这两长得很像,功能也几乎一样,我们放一起说。它们的作用都是“匹配选择器列表中的任何一个”,可以极大地简化我们的代码

场景

给文章里的所有标题(h1-h6)应用统一样式

以前我们怎么写?(又长又臭)

为了覆盖所有标题,我们得把它们一个个列出来

article h1,
article h2,
article h3,
article h4,
article h5,
article h6 {
  color: #333;
  margin-bottom: 1em;
}

要是想给这些标题的 hover 状态加个样式,还得再把上面这坨复制一遍……想想就头大

现在用 :is() 怎么写?(清爽多了)

/* One selector to rule them all */
article :is(h1, h2, h3, h4, h5, h6) {
  color: #333;
  margin-bottom: 1em;
}

/* Hover state is also super clean */
article :is(h1, h2, h3, h4, h5, h6):hover {
  color: #61dafb;
}

:where()

功能上,where()is() 一模一样。上面那个例子,你把 is 换成 where,效果完全不变

它们唯一的区别,也是最关键的区别,在于“权重” (Specificity)

这是什么意思?看个例子

:is(header, #main) p { color: blue; } /* #main 的权重是 100,所以这条规则权重是 101 */
:where(header, #main) p { color: red; } /* :where 权重是 0,所以这条规则权重是 1 */

p { color: green; } /* p 的权重是 1 */

什么时候用哪个?

浏览器兼容性

聊了这么多,现在(2025 年)到底能不能在项目里用呢?

答案是:放心用!

截至目前,所有主流现代浏览器(Chrome, Firefox, Safari, Edge)的最新版本都已良好支持这三个选择器

除非你还需要兼容非常非常老的浏览器(比如 IE,那就算了……),否则在你的新项目中,大胆地用起来吧